package com.weilive.core.netty.handler;

import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import org.jboss.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;
import com.weilive.core.command.Command;
import com.weilive.core.command.CommandManager;
import com.weilive.core.database.DynamicDataSource;
import com.weilive.core.event.EventDispatcher;
import com.weilive.core.netty.NettyErrors;
import com.weilive.core.netty.NettyEvents;
import com.weilive.core.netty.NettyException;
import com.weilive.core.netty.NettyRequestHandler;
import com.weilive.core.netty.http.HttpServlet;
import com.weilive.core.netty.http.HttpServletManager;
import com.weilive.core.protocol.json.JsonRequest;
import com.weilive.core.protocol.json.JsonRequestLocal;
import com.weilive.core.protocol.json.JsonResponse;
import com.weilive.core.protocol.protobuf.ProtobufRequest;
import com.weilive.core.protocol.protobuf.ProtobufRequestLocal;
import com.weilive.core.protocol.protobuf.ProtobufResponse;
import com.weilive.core.session.RoleContextHolder;
import com.weilive.core.utils.QueryStringDecoder;

public class NettyMultiWebRequestHandler extends NettyRequestHandler {

	private static final Logger log = LoggerFactory.getLogger(NettyMultiWebRequestHandler.class);

	public static final String UPLOAD_PATH = "/upload";
	public static final String COMMAND_PATH = "/command";
	public static final String WEBSOCK_PATH = "/socket";
	public static final String WEBSOCK_PROTOCOL = "default-protocol";

	// 缓存分片消息
	// private static final HashMap<Integer, ChannelBuffer> BinaryChannelBuffers
	// = new HashMap<Integer, ChannelBuffer>();
	// private static final HashMap<Integer, ChannelBuffer> TextChannelBuffers =
	// new HashMap<Integer, ChannelBuffer>();

	private WebSocketServerHandshaker handshaker = null;
	private static final ChannelFutureListener HANDSHAKE_LISTENER = new ChannelFutureListener() {
		public void operationComplete(ChannelFuture future) throws Exception {
			if (future.isSuccess()) {
				EventDispatcher.sendEvent(new NettyEvents.WebSocketHandshake(future.getChannel()));
			} else {
				EventDispatcher.sendEvent(new NettyEvents.OnError(future.getChannel(), future.getCause()));
				try {
					future.getChannel().close();
				} catch (Exception ex) {
				}
			}
		}
	};

	@Override
	public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
		EventDispatcher.sendEvent(new NettyEvents.Open(ctx.getChannel()));
	}

	@Override
	public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
		EventDispatcher.sendEvent(new NettyEvents.Close(ctx.getChannel()));
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
		EventDispatcher.sendEvent(new NettyEvents.OnError(e.getChannel(), e.getCause()));
		try {
			e.getChannel().close();
		} catch (Exception ex) {
		}
	}

	@Override
	public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
		DynamicDataSource.clear();
		JsonRequestLocal.clear();
		ProtobufRequestLocal.clear();
		RoleContextHolder.clear();
		long current = System.currentTimeMillis();
		Object message = e.getMessage();
		if (message instanceof HttpRequest) {
			HttpRequest request = (HttpRequest) message;
			String uri = request.getUri();
			if (uri.startsWith(WEBSOCK_PATH)) {
				String location = "ws://" + request.getHeader(HttpHeaders.Names.HOST) + WEBSOCK_PATH;
				WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(location, WEBSOCK_PROTOCOL, false);
				handshaker = wsFactory.newHandshaker(request);
				if (handshaker == null) {
					wsFactory.sendUnsupportedWebSocketVersionResponse(ctx.getChannel());
				} else {
					handshaker.handshake(ctx.getChannel(), request).addListener(HANDSHAKE_LISTENER);
				}
			} else if (uri.startsWith(COMMAND_PATH)) {
				handleCommandRequest(ctx.getChannel(), request);
			} else if (uri.startsWith(UPLOAD_PATH)) {
				// TODO
			} else {
				HttpServlet servlet = HttpServletManager.getServlet(uri);
				if (servlet != null) {
					handleHttpServletRequest(ctx.getChannel(), servlet, request);
				} else {
					handleHttpJsonRequest(ctx.getChannel(), request);
				}
			}
		} else if (message instanceof CloseWebSocketFrame) {
			handshaker.close(ctx.getChannel(), (CloseWebSocketFrame) message);
		} else if (message instanceof PingWebSocketFrame) {
			ctx.getChannel().write(new PongWebSocketFrame(((PingWebSocketFrame) message).getBinaryData()));
		}
		// ------------------------------
		else if (message instanceof TextWebSocketFrame) {
			handleSocketJsonRequest(JsonRequest.decode(ctx.getChannel(), ((TextWebSocketFrame) message).getText()));
		} else if (message instanceof BinaryWebSocketFrame) {
			handleSocketProtobufRequest(ProtobufRequest.decode(ctx.getChannel(), ((BinaryWebSocketFrame) message).getBinaryData()));
		}
		// ------------------------------
		EventDispatcher.sendEvent(new NettyEvents.OnMessage(System.currentTimeMillis() - current, ctx.getChannel(), message));
		log.warn("--------本次请求完成,耗时={}ms" , (System.currentTimeMillis() - current));
	}

	/**
	 * 发送http response
	 */
	private void sendHttpResponse(Channel channel, HttpResponse response) {
		channel.write(response).addListener(ChannelFutureListener.CLOSE);
	}

	private void handleCommandRequest(Channel channel, HttpRequest request) throws Exception {
		try {
			QueryStringDecoder decoder = HttpServlet.decodeQueryString(request);
			String result = CommandManager.command(new Command(decoder.get("name"), decoder.get("args"), decoder.get("user"), decoder.get("sign")));
			sendHttpResponse(channel, HttpServlet.createXmlResponse(result));
		} catch (NettyException e) {
			sendHttpResponse(channel, HttpServlet.createXmlResponse(JSONObject.toJSONString(e.getError())));
		} catch (Exception e) {
			sendHttpResponse(channel, HttpServlet.createXmlResponse("Server Error!"));
		}

	}

	private void handleHttpServletRequest(Channel channel, HttpServlet servlet, HttpRequest request) throws Exception {

		HttpResponse response = null;
		try {
			response = servlet.processRequest(request);
		} catch (NettyException e) {
			response = HttpServlet.createJsonResponse(JSONObject.toJSONString(e.getError()));
		} catch (Exception e) {
			e.printStackTrace();
			response = HttpServlet.createJsonResponse(JSONObject.toJSONString(NettyErrors.CODE_SERVER_ERROR));
		}
		if (response != null) {
			sendHttpResponse(channel, response);
		}
	}

	/**
	 * 处理http端的json request
	 */
	private void handleHttpJsonRequest(Channel channel, HttpRequest request) throws Exception {
		JsonRequest jsonRequst = JsonRequest.decode(channel, request);
		if (jsonRequst != null) {
			log.info("Receive http json request: " + jsonRequst);
			JsonRequestLocal.setRequest(jsonRequst);
			JsonResponse response = null;
			try {
				response = jsonRequst.handler.handleRequest(jsonRequst);
			} catch (NettyException e) {
				response = JsonResponse.create(e.getError(), jsonRequst.handler.getResponseType());
			} catch (Exception e) {
				e.printStackTrace();
				response = JsonResponse.create(NettyErrors.CODE_SERVER_ERROR, jsonRequst.handler.getResponseType());
			}
			if (response != null) {
				sendHttpResponse(channel, HttpServlet.createJsonResponse(request, response.encode()));
				log.info("Send http json response: " + response);
			}
		} else {
			sendHttpResponse(channel, HttpServlet.createXmlResponse(request, HttpResponseStatus.NOT_FOUND, "Not Found!"));
		}
	}

	/**
	 * 处理websocket端的json request
	 */
	private void handleSocketJsonRequest(JsonRequest request) throws Exception {
		if (request != null) {
			log.info("Receive socket json request: " + request);
			JsonRequestLocal.setRequest(request);
			JsonResponse response = null;
			try {
				response = request.handler.handleRequest(request);
			} catch (NettyException e) {
				response = JsonResponse.create(e.getError(), request.handler.getResponseType());
			} catch (Exception e) {
				e.printStackTrace();
				response = JsonResponse.create(NettyErrors.CODE_SERVER_ERROR, request.handler.getResponseType());
			}
			if (response != null) {
				request.channel.write(new TextWebSocketFrame(response.encode()));
				log.info("Send socket json response: " + response);
			}
		}
	}

	/**
	 * 处理websocket端的protobuf request
	 */
	private void handleSocketProtobufRequest(ProtobufRequest request) throws Exception {
		if (request != null) {
			log.info("Receive socket protobuf request: " + request);
			ProtobufRequestLocal.setRequest(request);
			ProtobufResponse response = null;
			try {
				response = request.handler.handleRequest(request);
			} catch (NettyException e) {
				response = ProtobufResponse.create(e.getError(), request.handler.getResponseType());
			} catch (Exception e) {
				e.printStackTrace();
				response = ProtobufResponse.create(NettyErrors.CODE_SERVER_ERROR, request.handler.getResponseType());
			}
			if (response != null) {
				request.channel.write(new BinaryWebSocketFrame(response.encode()));
				log.info("Send socket protobuf response: " + response);
			}
		}
	}
}
